/**
 *@file misc.c
 *@brief O arquivo misc.c contém funções que não estão diretamente
 * relacionadas a nenhum subssistema do jogo.
 *
 *@details No arquivo misc.c estão as funções de inicialização de
 * bibliotecas e de variáveis, e que também não estão diretamente
 * relacionadas a nenhum subssistema específico do jogo, como o
 * subssistema gráfico ou sonoro.
 *
 *@author
 * Copyright 2009 Vinícius
 *\n
 * This program is Free Software, distributed under the terms of the GNU General Public License, published by the Free Software Foundation; version 3 or any later version published by the Free Software Foundation, at your choice.
 *
 */

extern struct Anima * cozinheiro;
extern struct Anima * gato;

/**
 *@addtogroup misc
 *@{
 */

/**
 *@brief A função init deve ser a primeira função
 * a ser chamada. Ela inicializa todas as variáveis
 * globais, as principais variáveis e as bibliotecas
 * usadas pelo programa.
 *
 *@details A função init inicializa os subssistemas
 * VIDEO, TIMER e AUDIO, da biblioteca SDL e depois
 * tenta configurar o subssistema de vídeo. Primeiro
 * tentando configurar no modo em que usa a placa
 * de vídeo. Se algum erro ocorre na primeira
 * tentativa, é exibida uma menssagem de erro, e a
 * a variável tmp aponta para essa string. Depois
 * de imprimir a mensagem em stderr, esse ponteiro
 * é usado para liberar a memória alocada para a
 * string pela SDL.
 *
 *@return retorna 1 qualquer erro acontecer e 0 se
 * a operação for bem-sucedida.
 *
 *@warning Talvez não possamos desalocar a memória
 * apontada pela variável tmp, que foi alocada pela
 * SDL, então haverá um acesso ilegal de memória.
 *
 *@test É preciso testar se podemos desalocar a memória
 * apontada por tmp, que só é alocada se, primeiro,
 * a macro DEBUG estiver definida e, segundo, ocorra
 * uma falha na primeira tentativa de inicialização
 * do subssitema de vídeo.
 *\n
 * Para testar se realmente podemos desalocar tal memória
 * só precisamos forçar um erro na primeira tentativa de
 * inicialização da variável externa global tela.
 *\n
 * Para forçarmos esse erro podemos substituir o trecho de
 * código
 *\n
 *@code
 * tela = SDL_SetVideoMode(800,640,32,SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_ASYNCBLIT);
 * @endcode
 * por
 *@code
 * tela = SDL_SetVideoMode(800,640,48,SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_ASYNCBLIT);
 * @endcode
 * e se mesmo essa modificação não for suficiente, tente
 *@code
 * tela = SDL_SetVideoMode(0,0,32,SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_ASYNCBLIT);
 * @endcode
 *
 *@todo Testar a desalocação de memória.
 *\n
 *Ler e \<talvez\> implementar SDL_EnableUNICODE, função da SDL.
 *
 */
int init(){
/**
 *@{
 */
	atexit(sair);
#	ifdef RELEASE
		printf("Eater-Thief, versão %s\n",VERSAO);
#	elif defined DEBUG
		printf("Eater-Thief, versão %s-DEBUG\n",VERSAO);
#	endif
#	ifdef SAFE_MODE
		printf("Iniciando em safe-mode\n");
#	endif
	if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTTHREAD|SDL_INIT_AUDIO)==-1){
		fprintf(stderr,"Falha ao inicializar a SDL\nErro: %s.",SDL_GetError());
		return 1;
	}
	gfx_prepare();
	sfx_init();
// SDL_ShowCursor(SDL_DISABLE);
// SDL_EnableUNICODE();//Habilita caracteres (para o sistema de entrada de teclado) extras como acentos, mas ainda falta alguém ler o protótipo da função
	srand(time(0));
	return 0;
/**
 *@}
 */
}

/**
 *@brief Cria um mapa aleatoriamente.
 *
 *@details A função init_map cria um mapa aleatório e deve
 * ser usada para inicializar mapas. Os mapas devem ser
 * arrays[25][20].
 *
 *@param[out] mapa O endereço para o primeiro elemento um
 * array multidimensional 25x20, que conterá o mapa gerado.
 *
 *@param[out] npeixes Se não for NULL, um valor inteiro
 * será armazenado neste endereço representando o número
 * de peixes que o mapa criado tem.
 *
 *@todo Implementar um sistema de níveis de dificuldade e
 * implementar um algoritmo de geração aleatória de mapas
 * mais eficiente.
 *\n
 * Fazer com que a função retorne o número de peixes que
 * há no mapa gerado.
 *
 */
void init_map(Tile_t (* const mapa)[20], int * npeixes){
/**
 *@{
 */
	if(npeixes != NULL){
		*npeixes = 0;
	}
	register int aux,i,j;
	for(i=1;i<19;++i){
		for(j=1;j<24;++j){
			mapa[j][i] = (rand()%4)?17:-1;
			if(npeixes != NULL){
				if(mapa[j][i]==17) ++(*npeixes);
			}
		}
	}
	for(i = 0;i < 25;++i){
		mapa[i][0] = -1;
		mapa[i][19] = -1;
	}
	for(i = 0;i < 20;++i){
		mapa[0][i] = -1;
		mapa[24][i] = -1;
	}

	if(mapa[0][0] == -1){
		aux = 0;
		if(mapa[1][0] == -1)//leste
			aux |= 2;
		if(mapa[0][1] == -1)//sul
			aux |= 4;
		mapa[0][0] = aux + 1;
	}
	if(mapa[24][0] == -1){
		aux = 0;
		if(mapa[24][1] == -1)//sul
			aux |= 4;
		if(mapa[23][0] == -1)//oeste
			aux |= 8;
		mapa[24][0] = aux + 1;
	}
	if(mapa[0][19] == -1){
		aux = 0;
		if(mapa[0][18] == -1)//norte
			aux |= 1;
		if(mapa[1][19] == -1)//leste
			aux |= 2;
		mapa[0][19] = aux + 1;
	}
	if(mapa[24][19] == -1){
		aux = 0;
		if(mapa[24][18] == -1)//norte
			aux |= 1;
		if(mapa[23][19] == -1)//oeste
			aux |= 8;
		mapa[24][19] = aux + 1;
	}
	for(i=1;i<24;++i){
		if(mapa[i][0] != -1)
			continue;
		aux = 0;
		if((mapa[i+1][0] == -1)||((mapa[i+1][0] >= 1)&&(mapa[i+1][0] <= 16)))//leste
			aux |= 2;
		if((mapa[i][1] == -1)||((mapa[i][1] >= 1)&&(mapa[i][1] <= 16)))//sul
			aux |= 4;
		if((mapa[i-1][0] == -1)||((mapa[i-1][0] >= 1)&&(mapa[i-1][0] <= 16)))//oeste
			aux |= 8;
		mapa[i][0] = aux + 1;
	}
	for(i=1;i<24;++i){
		if(mapa[i][19] != -1)
			continue;
		aux = 0;
		if((mapa[i][18] == -1)||((mapa[i][18] >= 1)&&(mapa[i][18] <= 16)))//norte
			aux |= 1;
		if((mapa[i+1][19] == -1)||((mapa[i+1][19] >= 1)&&(mapa[i+1][19] <= 16)))//leste
			aux |= 2;
		if((mapa[i-1][19] == -1)||((mapa[i-1][19] >= 1)&&(mapa[i-1][19] <= 16)))//oeste
			aux |= 8;
		mapa[i][19] = aux + 1;
	}
	for(j=1;j<19;++j){
		if(mapa[0][j] != -1)
			continue;
		aux=0;
		if((mapa[0][j-1] == -1)||((mapa[0][j-1] >= 1)&&(mapa[0][j-1] <= 16)))//norte
			aux |= 1;
		if((mapa[1][j] == -1)||((mapa[1][j] >= 1)&&(mapa[1][j] <= 16)))//leste
			aux |= 2;
		if((mapa[0][j+1] == -1)||((mapa[0][j+1] >= 1)&&(mapa[0][j+1] <= 16)))//sul
			aux |= 4;
		mapa[0][j] = aux + 1;
	}
	for(j=1;j<19;++j){
		if(mapa[24][j] != -1)
			continue;
		aux=0;
		if((mapa[24][j-1] == -1)||((mapa[24][j-1] >= 1)&&(mapa[24][j-1] <= 16)))//norte
			aux |= 1;
		if((mapa[24][j+1] == -1)||((mapa[24][j+1] >= 1)&&(mapa[24][j+1] <= 16)))//sul
			aux |= 4;
		if((mapa[23][j] == -1)||((mapa[23][j] >= 1)&&(mapa[23][j] <= 16)))//oeste
			aux |= 8;
		mapa[24][j] = aux + 1;
	}
	for(j=1;j<19;++j){
		for(i=1;i<24;++i){
			if(mapa[i][j] != -1)
				continue;
			aux = 0;
			if((mapa[i][j-1] == -1)||((mapa[i][j-1] >= 1)&&(mapa[i][j-1] <= 16)))//norte
				aux |= 1;
			if((mapa[i+1][j] == -1)||((mapa[i+1][j] >= 1)&&(mapa[i+1][j] <= 16)))//leste
				aux |= 2;
			if((mapa[i][j+1] == -1)||((mapa[i][j+1] >= 1)&&(mapa[i][j+1] <= 16)))//sul
				aux |= 4;
			if((mapa[i-1][j] == -1)||((mapa[i-1][j] >= 1)&&(mapa[i-1][j] <= 16)))//oeste
				aux |= 8;
			mapa[i][j] = aux + 1;
		}
	}

	i = 12;//  :=(25-1)/2
	j = 11;//  :=(20-1)/2
	{
		short int k1=0,k2=-1;
		int k=0,lastk=-1;
		enum Dir_t dir=norte;
		struct Anima * atual = cozinheiro;
		for(aux=0;aux<NUMERO_DE_COZINHEIROS;++aux){
			while(mapa[i][j]!=17){
				if(k==lastk){
					if(dir&1){//direção horizontal
						++k;
					}
					lastk = 0;
				}else{
					++lastk;
				}
				i += k1;
				j += k2;
				if(!lastk){
					switch(dir){
						case norte:
							dir = leste;
							k1 = 1;
							k2 = 0;
							break;
						case leste:
							dir = sul;
							k1 = 0;
							k2 = 1;
							break;
						case sul:
							dir = oeste;
							k1 = -1;
							k2 = 0;
							break;
						case oeste:
							dir = norte;
							k1 = 0;
							k2 = -1;
							break;
					}
				}
			}
			if(atual == NULL){
				cozinheiro = malloc(sizeof(struct Anima));
				atual = cozinheiro;
				atual->proximo = NULL;
				atual->anterior = NULL;
			}else{
				while(atual->proximo != NULL){
					atual = atual->proximo;
				}
				atual->proximo = malloc(sizeof(struct Anima));
				atual->proximo->anterior = atual;
				atual = atual->proximo;
				atual->proximo = NULL;
			}
			atual->dir = indefinido;
			atual->tile = 19;
			atual->x_fake = i;
			atual->y_fake = j;
			atual->x = i*32;
			atual->y = j*32;
		}
	}

	if(gato == NULL){
		gato = malloc(sizeof(struct Anima));
		gato->anterior = NULL;
		gato->proximo = NULL;
	}
	gato->tile = 0;
	for(j=1;j<20;++j){
		for(i=(j-1)?0:1;i<25;++i){
			if(mapa[i][j] == 17){
				gato->tile = 18;
				gato->x_fake = i;
				gato->y_fake = j;
				gato->x = i*32;
				gato->y = j*32;
				gato->dir = indefinido;
				mapa[i][j] = 0;
				if(npeixes != NULL)
					--(*npeixes);
				break;
			}
		}
		if(gato->tile == 18){
			break;
		}
	}
	gfx_cartografo();
	return;
/**
 *@}
 */
}


/**
 *@brief Função que deve ser chamada antes de finalizar
 * o programa.
 *@warning Essa função é chamada quando se faz qualquer
 * chamada a função exit, então dê preferência chamar
 * a função exit em vez de chamar sair.
 */
void sair(){
/**
 *@{
 */
	destruir_pilhas();
	gfx_sair();
	sfx_quit();
	SDL_Quit();
/**
 *@}
 */
}

/**
 *@brief Aponta para o primeiro elemento do grupo
 * de anima.
 *@details A partir de \p qualquer, calcula o
 * ponteiro para o primeiro integrante.
 *@param qualquer um ponteiro para o grupo de animas
 *@return Retorna um ponteiro para o primeiro elemento
 * do grupo.
 */
struct Anima * primeiro(struct Anima * qualquer){
/**
 *@{
 */
	if(qualquer == NULL){
		return NULL;
	}else{
		struct Anima * aux = qualquer;
		while(aux->anterior != NULL){
			aux = aux->proximo;
		}
		return aux;
	}
/**
 *@}
 */
}

/**
 *@brief Função usada para calcular o próximo
 * anima do grupo de animas apontado por atual.
 *@param atual Ponteiro para o anima (ou grupo
 * de animas).
 *@return Retorna o ponteiro para o próximo
 * anima.
 */
struct Anima * proximo(struct Anima * atual){
/**
 *@{
 */
	if(atual != NULL){
		return atual->proximo;
	}else{
		return NULL;
	}
/**
 *@}
 */
}

/**
 *@brief Calcula as coordenadas fake.
 *@details Lê as coordenadas reais de
 * \p anima e calcula as coordenadas
 * fakes. As coordenadas fake são
 * diretamente inseridas em \p anima .
 * Não havendo assim necessidade de
 * retorno.
 */
void calculo_fake_cors(struct Anima * anima){
/**
 *@{
 */
	anima->x_fake = anima->x / 32 + ((anima->x % 32 > 15) ? 1 : 0);
	anima->y_fake = anima->y / 32 + ((anima->y % 32 > 15) ? 1 : 0);
/**
 *@}
 */
}

inline Bool_t colidex(int x1,int x2){
	if(x1 < x2){
		if((x1 + 32) > x2){
			return TRUE;
		}
	}else{
		if((x2 + 32) > x1){
			return TRUE;
		}
	}
	return FALSE;
}

inline Bool_t colidey(int y1,int y2){
	if(y1 < y2){
		if((y1 + 32) > y2){
			return TRUE;
		}
	}else{
		if((y2 + 32) > y1){
			return TRUE;
		}
	}
	return FALSE;
}

/**
 *@brief Testa a colisão
 *@details Função que testa se há colisão. Exemplo
 *@code
 * if(colide(gato->x,gato->y,cozinheiro->x,cozinheiro ...
 * @endcode
 */
Bool_t colide(int x1,int y1,int x2,int y2){
/**
 *@{
 */
	if(colidex(x1,x2)&&colidey(y1,y2)){
		return TRUE;
	}
	return FALSE;
/**
 *@}
 */
}

/**
 *@}
 */
